#!/usr/bin/python -S
"""
highlight.py
"""

__author__ = 'Andy Chu'


import cgi
import re
from readonly import jsontemplate
import jsonpattern
from jsonpattern import tokenize


_TEMPLATE = jsontemplate.Template(
    '<span style="color: {color|htmltag};">{text|html}</span>')

_SPECIAL_TEMPLATE = jsontemplate.Template(
    '<span class="special">{text|html}</span>')

_COMMENT_TEMPLATE = jsontemplate.Template(
    '<span class="comment">{text|html}</span>')

_LINE_TEMPLATE = jsontemplate.Template(
    '<span class="special">{prefix|raw}|</span>{highlighted-line|raw}')


def FormatLine(line):

  # TODO: This would be a good candidate for some awk-like pattern matching

  if line == {}:
    return ''
  elif 'comment' in line:
    return _COMMENT_TEMPLATE.expand(text=line['comment'])
  else:
    return _LINE_TEMPLATE.expand(line)


_PATTERN_TEMPLATE = jsontemplate.Template("""\
<pre>
{.repeated section lines}
  {@|format-line}
{.end}
</pre>
""", more_formatters={'format-line': FormatLine})

# e.g. 'value foo', '$optional bar'
BLOCK_PATTERN = jsonpattern.Pattern(r"""
P|   { \s* space1 }
 |   $optional keyword
P|       \$?(optional|choice|value|repeated|end|or)
 |   end
 |   optional @
P|     { \s* space2 }
P|     { [\w\-]+ key }
 |   end
P|   { .* rest }
""")

BLOCK_TEMPLATE = jsontemplate.Template('\
{space1}<b>{keyword}</b>{space2}<span style="color: blue">{key}</span>{rest}', undefined_str='')

SUBPATTERN_PATTERN = jsonpattern.Pattern(
    r"P|   \{ { :[\w\-:]+ subpattern } [ ] { [\w\-:]+ key } \}")

SUBPATTERN_TEMPLATE = jsontemplate.Template(
    '<span class="special">'
    '{.meta-left}<span class="subpattern">{subpattern}</span> '
    '<span style="color: blue">{key}</span>{.meta-right}'
    '</span>')

# HACK: The regular expressions aren't \S+.  The rule is based on the first
# char.
SHORTHAND_PATTERN = jsonpattern.Pattern(
    r"P|   \{ {/[ ]\S+/ subpattern} [ ] {/[\w\-:@]+[ ]/ key} { .* rest }")

SHORTHAND_TEMPLATE = jsontemplate.Template(
    '<span class="special">'
    '{.meta-left}<span style="color: black">{subpattern}</span> '
    '<span style="color: blue">{key}</span>'
    '{rest}'
    '</span>')


class Error(Exception):
  """Base class for exceptions thrown by this module."""


def AsHtml(pattern_str):
  """Highlight a JSON Pattern stirng as HTML."""

  data = []

  for line in pattern_str.splitlines():
    #line = line.replace('\n', '<br/>')

    stripped = line.strip()
    if not stripped:
      data.append({})
      continue

    if stripped.startswith('#'):
      data.append({'comment': line})
      continue

    try:
      prefix, rest = line.split('|', 1)
    except ValueError:
      print 'Line has no prefix: %r' % line
      data.append({'prefix': '', 'highlighted-line': line})
      continue

    if not rest.strip():
      data.append({'prefix': prefix, 'highlighted-line': rest})
      continue

    line_data = {'prefix': prefix}
    
    if prefix.isspace():  # Block
      result = BLOCK_PATTERN.match(rest)
      if result.data:
        h = BLOCK_TEMPLATE.expand(result.data)
        data.append({'prefix': prefix, 'highlighted-line': h})
        continue

      raise Error("Couldn't recognize block %r\n%s\n" %
          (rest, '\n'.join(result.failures)))

    elif 'P' in prefix:  # Normal pattern
      line_data['highlighted-line'] = HighlightPattern(rest)
      data.append(line_data)

    elif 'S' in prefix:  # Significant whitespace
      line_data['highlighted-line'] = HighlightPattern(rest)
      data.append(line_data)

    elif 'I' in prefix:  # Insignificant space
      line_data['highlighted-line'] = rest
      data.append(line_data)

    elif "'" in prefix:  # Quote
      line_data['highlighted-line'] = rest
      data.append(line_data)

    else:
      raise Error('Invalid line prefix %r' % prefix)

  return _PATTERN_TEMPLATE.expand({'lines': data})


def HighlightPattern(s):
  tokens = tokenize.SPLIT_RE.split(s)
  html = []
  for i, token in enumerate(tokens):
    if i % 2 == 0:
      html.append(cgi.escape(token))
    else:
      if token.startswith('{#'):
        html.append(_COMMENT_TEMPLATE.expand(text=token))
        continue

      result = SUBPATTERN_PATTERN.match(token)
      if result.data:
        html.append(SUBPATTERN_TEMPLATE.expand(result.data))
        continue

      result = SHORTHAND_PATTERN.match(token)
      if result.data:
        html.append(SHORTHAND_TEMPLATE.expand(result.data))
        continue

      html.append(_SPECIAL_TEMPLATE.expand(text=token))

  return ''.join(html)
